package net.cyndeline.scalarlib.rldungeon.dgs.strategy.pointlessArea.help

import scalax.collection.immutable.Graph
import net.cyndeline.scalarlib.rldungeon.dgs.strategy.help.{CollapsedEdge, CollapsedNode}
import scala.util.Random
import net.cyndeline.scalarlib.rldungeon.dgs.graph.Room
import com.escalatesoft.subcut.inject.{BindingModule, Injectable}
import net.cyndeline.scalarlib.rldungeon.dgs.strategy.pointlessArea.PointlessNodeFinder
import net.cyndeline.scalarlib.util.{RandomElementFinderInterface, RandomElementFinder}

/**
 * Selects random pointless nodes among those that exist on the edge of branches in a cycle-collapsed tree.
 *
 * @constructor Creates a new maximum distance node finder.
 */
class MaximumDistancePointlessNode[VType <: Room[VType]](random: Random)(implicit val bindingModule: BindingModule) extends PointlessNodeFinder[VType] with Injectable {
  private val elementFinder = injectOptional[RandomElementFinderInterface] getOrElse { new RandomElementFinder() }

  /**
   * Finds the set of all pointless nodes and selects one at random.
   *
   * @param graph The graph to find a pointless node in.
   * @param validMainAreaConnection The set of nodes along the main path in the graph that the pointless area
   *                                of the node may be connected to.
   * @return A random pointless node, or None of no pointless nodes exist or if every pointless node has a main area
   *         connection that isn't present in the submitted set.
   */
  override def findActivatorNode(graph: Graph[CollapsedNode[VType], CollapsedEdge[VType]#Inner], validMainAreaConnection: Set[CollapsedNode[VType]]): Option[CollapsedNode[VType]] = {

    /* Every node that contains pointless vertices, is at the end of the collapsed tree structure and is
     * connected to the main path using a valid connection node.
     */
    val allPointlessEndNodes = findPointlessActivatorEndNodes(graph, validMainAreaConnection)

    if (allPointlessEndNodes.isEmpty) {
      None
    } else {
      Option(elementFinder.findRandomElement(allPointlessEndNodes, random))
    }
  }

  /**
   * Searches for a random pointless edge node whose only requirement is that it can hold an additional reward.
   * @param graph Graph to search for the pointless node in.
   * @return A pointless node that can host one more reward, or none if no such nodes exist.
   */
  override def findRewardNode(graph: Graph[CollapsedNode[VType], CollapsedEdge[VType]#Inner]): Option[CollapsedNode[VType]] = {
    var rewardNodes = Set[CollapsedNode[VType]]()

    graph.nodes.foreach(n => {
      if (n.degree == 1 && n.isPointless && n.canHoldRewards) {
        val outer: CollapsedNode[VType] = n
        rewardNodes += outer
      }
    })

    if (rewardNodes.isEmpty) {
      None
    } else {
      Option(elementFinder.findRandomElement(rewardNodes, random))
    }
  }

  /**
   * Finds every node that is pointless, is at the end of a tree branch in the level, and is connected to the
   * main path using a node that is in the specified collection.
   */
  private def findPointlessActivatorEndNodes(graph: Graph[CollapsedNode[VType], CollapsedEdge[VType]#Inner],
                                    validMainAreaConnection: Set[CollapsedNode[VType]]): Set[CollapsedNode[VType]] = {

    val allPointlessEndNodes = graph.nodes.filter(n => {
      if (n.degree == 1 && n.isPointless && n.canHoldActivators) {
        val somePointlessVertex = n.allPointlessVertices.head // Guaranteed to exist if the node is pointless
        val pointlessData = somePointlessVertex.pointlessAreaData.get
        val connection = pointlessData.mainAreaConnection
        validMainAreaConnection.exists(n => n.contains(connection))

      } else {
        false
      }
    })

    val result: Set[CollapsedNode[VType]] = allPointlessEndNodes.map(n => {
      val collapsed: CollapsedNode[VType] = n
      collapsed
    }).toSet

    result
  }
}
